#include <TError.h>
// #include <TString.h>
TString fAliRootMode = "";
TString fAliRootExtraLibs = "";
TString fAliRootExtraInclude = "";
Int_t fMyDebug = 0;
Int_t fMyNumberAliEnRetry = 3;
Bool_t fEnableAliEn = kFALSE;
TString fBadWorker = "";
TString fAlirootVer = "";
TString fAlienPackagesDir = "";
TString fPackageName = "";
Bool_t fIsProofLite=kFALSE;

Int_t SETUP(TList *input = 0) {

  aafHandleInputList(input);

  if (!fBadWorker.CompareTo(gSystem->HostName())) {
    ::Error(gSystem->HostName(), "This is bad worker.");
    return -1;
  }

  // sets correct aliroot path for aaf
  fAlirootVer = gSystem->BaseName(gSystem->pwd());
  fAlienPackagesDir = gSystem->Getenv("ALICE_PROOF_AAF_ALIEN_PACKAGES");
  aafPrintAAFDebug(Form("$ALICE_PROOF_AAF_ALIEN_PACKAGES is %s", fAlienPackagesDir.Data()), 2);

  fPackageName = fAlirootVer.Data();
	if (!fPackageName.CompareTo("AliRootProofLite")) fIsProofLite=kTRUE;
	
  // running on the local client
  if ((gProof && !gProof->IsMaster()) || fIsProofLite ) {
    if ((TString(gSystem->Getenv("ALICE_ROOT")).Length()) <= 0) {
      ::Error(gSystem->HostName(), "$ALICE_ROOT not set on the client.");
      return -1;
    } else {
      ::Info(gSystem->HostName(), Form("Enabling AliRoot on client from \"%s\" ...", gSystem->Getenv("ALICE_ROOT")));
			aafSetMacroPath(gSystem->Getenv("ALICE_ROOT"));
      if (aafLoadLibraries() < 0) {
        ::Error(gSystem->HostName(), "Loading libs failed on client. Returning -1");
        return -1;
      }
      gSystem->AddIncludePath(Form("-I\"%s/include\"", gSystem->Getenv("ALICE_ROOT")));
		  gROOT->ProcessLine(Form(".include %s/include",gSystem->Getenv("ALICE_ROOT")));

    }
  }
  // running on the PROOF nodes
  else {
    if (fAlienPackagesDir.IsNull()) {
      ::Error(gSystem->HostName(), "Couldn't find ALICE_PROOF_AAF_ALIEN_PACKAGES !!!");
      return -1;
    }
    ::Info(gSystem->HostName(), Form("Enabling %s ...", fAlirootVer.Data()));
    fAlirootVer.ReplaceAll("@", "/");
    fAlirootVer.ReplaceAll("::", "/");
    TString alirootlocation(Form("%s/%s/%s", fAlienPackagesDir.Data(), fAlirootVer.Data(), gSystem->BaseName(fAlirootVer.Data())));
    gSystem->Setenv("ALICE_ROOT", alirootlocation.Data());
		aafSetMacroPath(gSystem->Getenv("ALICE_ROOT"));
    aafSetLIBRARYPath(alirootlocation);
    if (aafLoadLibraries() < 0) {
      ::Error(gSystem->HostName(), "Loading libs failed. Returning -1");
      return -1;
    }
    gSystem->AddIncludePath(Form("-I\"%s/include\"", gSystem->Getenv("ALICE_ROOT")));
		gROOT->ProcessLine(Form(".include %s/include",gSystem->Getenv("ALICE_ROOT")));
	}

  aafPrintAAFDebug(Form("ALIROOT_EXTRA_INCLUDES is '%s'", fAliRootExtraInclude.Data()), 2);
  if (!fAliRootExtraInclude.IsNull())
    aafaafAddIncludePathFromLibss(fAliRootExtraInclude.Data());

  aafPrintAAFDebug(Form("Include Path is %s", gSystem->GetIncludePath()), 1);


  ::Info(gSystem->HostName(), Form("Enabling %s was OK ...", fPackageName.Data()));

  if (aafDoEnableAliEn(fEnableAliEn)) {
    aafPrintAAFDebug("Returning 100", 1);
    return -1;
  }

  aafPrintAAFDebug("Returning 0", 1);
  return 0;

}

void aafHandleInputList(TList *input) {
  if (!input) return;
  TIter next((TList*)input);
  TNamed *obj;
  while ((obj = dynamic_cast<TNamed*>(next()))) {
    if (!obj) continue;
    aafHandleInputItem(obj->GetName(), obj->GetTitle());
  }
}

void aafHandleInputItem(TString name, TString value) {
  aafPrintAAFDebug(Form("aafHandleInputItem(%s,%s)", name.Data(), value.Data()), 3);
  name.ToUpper();
  if (!name.CompareTo("ALIROOT_MODE")) {
    fAliRootMode = value.Data();
  } else if (!name.CompareTo("ALIROOT_EXTRA_LIBS")) {
    fAliRootExtraLibs = value.Data();
  } else if (!name.CompareTo("ALIROOT_EXTRA_INCLUDES")) {
    fAliRootExtraInclude = value.Data();
  } else if (!name.CompareTo("ALIROOT_AAF_BAD_WORKER")) {
    fBadWorker = value.Data();
  } else if (!name.CompareTo("ALIROOT_AAF_DEBUG")) {
    if (value.IsDigit()) {
      fMyDebug = value.Atoi();
    } else {
      fMyDebug = 0;
    }
  } else if (!name.CompareTo("ALIROOT_ENABLE_ALIEN")) {
    if (!value.IsNull())
      fEnableAliEn = kTRUE;
  } else if (!name.CompareTo("ALIROOT_ALIEN_RETRY")) {
    if (value.IsDigit()) {
      fMyNumberAliEnRetry = value.Atoi();
    } else {
      fMyNumberAliEnRetry = 3;
    }
  }
}

Int_t aafSetLIBRARYPath(TString alirootlocation) {
  // Add AliRoot libraries path
  //
  TString xlib =  Form("%s/lib/tgt_%s", alirootlocation.Data(), gSystem->GetBuildArch());
  TString ldpath = gSystem->GetDynamicPath();

  // In the first position after the working dir
  Int_t pos = 0;
  if (ldpath.BeginsWith(".:")) pos = 2;
  if (ldpath.Index(xlib) == kNPOS) {
    ldpath.Insert(pos, Form(".:%s:", xlib.Data()));
    gSystem->SetDynamicPath(ldpath);
  }

	return 0;
}

Int_t aafLoadLibraries() {

  fAliRootMode.ToUpper();

  // load all ROOT libs
  if (aafLoadMoreLibsAtOnce("VMC:Tree:Physics:Matrix:Minuit:XMLParser:Gui") < 0) return -1;

  TString alllibs;
  Int_t retVal = 0;
  // handle aliroot modes
  if (!fAliRootMode.CompareTo("ALIROOT")) {
    ::Info(gSystem->HostName(), Form("Running in %s mode ...", fAliRootMode.Data()));
    aafPrintAAFDebug(Form("Loading Macro %s ...", gSystem->ExpandPathName("$ALICE_ROOT/macros/loadlibs.C")), 2);
    retVal = gROOT->Macro(gSystem->ExpandPathName("$ALICE_ROOT/macros/loadlibs.C"));
    if (retVal) {
      aafPrintPossibleROOTVersion();
      return -1;
    }
  } else if (!fAliRootMode.CompareTo("SIM")) {
    ::Info(gSystem->HostName(), Form("Running in %s mode ...", fAliRootMode.Data()));
    aafPrintAAFDebug(Form("Loading Macro %s ...", gSystem->ExpandPathName("$ALICE_ROOT/macros/loadlibssim.C")), 2);
    retVal = gROOT->Macro(gSystem->ExpandPathName("$ALICE_ROOT/macros/loadlibssim.C"));
    if (retVal) {
      aafPrintPossibleROOTVersion();
      return -1;
    }
  } else if (!fAliRootMode.CompareTo("REC")) {
    ::Info(gSystem->HostName(), Form("Running in %s mode ...", fAliRootMode.Data()));
    aafPrintAAFDebug(Form("Loading Macro %s ...", gSystem->ExpandPathName("$ALICE_ROOT/macros/loadlibsrec.C")), 2);
    retVal = gROOT->Macro(gSystem->ExpandPathName("$ALICE_ROOT/macros/loadlibsrec.C"));
    if (retVal) {
      aafPrintPossibleROOTVersion();
      return -1;
    }
  } else {
    fAliRootExtraLibs.ReplaceAll("ANALYSISalice:", "");
    fAliRootExtraLibs.ReplaceAll("OADB:", "");
    fAliRootExtraLibs.ReplaceAll("ANALYSIS:", "");
    alllibs = "STEERBase:ESD:AOD:ANALYSIS:OADB:ANALYSISalice:";
  }

  alllibs += fAliRootExtraLibs;

  // Load Analysis Framework libraries
  //
  if (aafLoadMoreLibsAtOnce(alllibs) < 0) {
    aafPrintPossibleROOTVersion();
    return -1;
  }

  return 0;
}

void aafPrintPossibleROOTVersion() {

  ::Error(gSystem->HostName(), "++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
  ::Error(gSystem->HostName(), Form("AliRoot %s is not compatible with current ROOT version ...", fPackageName.Data()));
  ::Error(gSystem->HostName(), Form("Searching ROOT version for AliRoot %s ...", fPackageName.Data()));
  TString rootVerStr = gSystem->GetFromPipe(Form("cat %s/deps/aliroot_deps.txt | grep \"%s|\"| awk -F '|' '{print $2}'", fAlienPackagesDir.Data(), fPackageName.Data()));
  if (rootVerStr.IsNull()) {
    ::Error(gSystem->HostName(), Form("ROOT version for AliRoot %s was not found !!! Please report it to AAF admin ...", fPackageName.Data()));
    return;
  } else {
    ::Error(gSystem->HostName(), Form("ROOT version for AliRoot %s found ...", fPackageName.Data()));
    ::Error(gSystem->HostName(), Form("\tROOT version is '%s' ...", rootVerStr.Data()));
//     ::Error(gSystem->HostName(),"");
//     ::Error(gSystem->HostName(),"You may do following :");
//     ::Error(gSystem->HostName(),Form("\tTProof::Mgr(\"%s\")->SetROOTVersion(\"%s\");",gProof->GetMaster(), rootVerStr.Data()));
//     ::Error(gSystem->HostName(),Form("\tTProof::Open(\"%s\");",gProof->GetMaster()));
//     ::Error(gSystem->HostName(),"");
  }
  ::Error(gSystem->HostName(), "++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
}

Int_t aafLoadMoreLibsAtOnce(TString alllibs) {

  TObjArray* tokens = alllibs.Tokenize(":");
  TIter iter(tokens);
  TObjString* lib = 0;
  TString libstr;
  while (lib = (TObjString*) iter.Next()) {
    libstr = lib->GetString();
    if (libstr.IsNull()) continue;
    if (libstr.BeginsWith("lib")) libstr.Remove(0, 3);
    if (libstr.EndsWith(".so")) libstr.Remove(libstr.Length() - 3, libstr.Length());
    ::Info(gSystem->HostName(), Form("Loading lib%s.so ...", libstr.Data()));
    if (aafCheckLoadLibrary(Form("lib%s.so", libstr.Data())) < 0) {
      ::Error(gSystem->HostName(), Form("lib%s.so not found or mismatch.", libstr.Data()));
      return -1;
    }
  }
  return 0;
}

Int_t aafCheckLoadLibrary(TString library) {
  // checks if a library is already loaded, if not loads the library
  //
  if (strlen(gSystem->GetLibraries(Form("%s", library.Data()), "", kFALSE)) > 0)
    return 1;

  return gSystem->Load(Form("%s", library.Data()));
}

void aafaafAddIncludePathFromLibss(TString libsStr = "CORRFW:PWG2/RESONANCES") {

  //
  TObjArray* tokens = libsStr.Tokenize(":");
  TIter iter(tokens);
  TObjString* lib = 0;
  TString libstr;
  while (lib = (TObjString*) iter.Next()) {
    libstr = lib->GetString();
    if (libstr.IsNull()) continue;
    aafAddIncludePathFromLibs(libstr.Data());
  }
}

void aafAddIncludePathFromLibs(TString includePath = "PWG2/RESONANCES") {
  aafPrintAAFDebug(Form("-I\"%s/%s\"", gSystem->Getenv("ALICE_ROOT"), includePath.Data()), 3);
  gSystem->AddIncludePath(Form("-I\"%s/%s\"", gSystem->Getenv("ALICE_ROOT"), includePath.Data()));
  gROOT->ProcessLine(Form(".include %s/%s",gSystem->Getenv("ALICE_ROOT"), includePath.Data()));

}

void aafPrintAAFDebug(TString message, Int_t debugLevel = 10) {
  if (fMyDebug >= debugLevel) {
    Printf(Form("Debug %d <%s> : %s", debugLevel, gSystem->HostName(), message.Data()));
  }
}

Int_t aafDoEnableAliEn(Bool_t enableAliEn = kTRUE) {
  if (enableAliEn) {
    if (gProof && !gProof->IsMaster()) {
      ::Info(gSystem->HostName(), "Enabling AliEn on workers (it may take while)...");
    } else {
      ::Info(gSystem->HostName(), "Enabling AliEn ...");
      Int_t retry = 0;
      while (retry < fMyNumberAliEnRetry) {

        TGrid::Connect("alien://");
        if (gGrid) {
          ::Info(gSystem->HostName(), "AliEn enabled ...");
          return 0;
        } else {
          ::Warning(gSystem->HostName(), Form("Retry %d out of %d ...", retry + 1, fMyNumberAliEnRetry));
        }
        retry++;
        gSystem->Sleep(100);
      }
      if (!gGrid) {
        ::Error(gSystem->HostName(), Form("Problem with connecting to AliEn after %d retry ...", fMyNumberAliEnRetry));
        return 100;
      }
    }
  }
  return 0;
}

void aafSetMacroPath(TString alirootPath) {
	TString macroPath = gROOT->GetMacroPath();
  macroPath += Form(":%s/macros", alirootPath.Data());
  gROOT->SetMacroPath(macroPath);  
}
